package com.cyf.common.core.coll;

import cn.hutool.core.comparator.CompareUtil;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author chenyifan
 * @create 2024-08-09 15:28
 */
public class CollUtil {

    // ---------------------------------------- 基础方法 -----------------------------------------
    /**
     * 反序给定List，会在原List基础上直接修改
     * @param <T>  元素类型
     * @param list 被反转的List
     */
    public static <T> void reverse(List<T> list) {
        Collections.reverse(list);
    }

    /**
     * 反序给定List，会创建一个新的List，原List数据不变
     * @param <T>  元素类型
     * @param list 被反转的List
     * @return 反转后的新列表
     */
    public static <T> List<T> reverseNew(List<T> list) {
        List<T> newList = new ArrayList<>(list);
        Collections.reverse(newList);
        return newList;
    }


    /**
     * 求补集，属于全集但不属于集合A
     * @param all 全集
     * @param collA 集合A
     * @return
     */
    public static <T> List<T> diff(Collection<T> all, Collection<T> collA) {
        if (CollectionUtils.isEmpty(collA)) {
            return new ArrayList<>(all);
        }
        return all.stream().filter(ele -> !collA.contains(ele)).collect(Collectors.toList());
    }

    /**
     * 求补集，属于全集但不属于集合A
     * @param all 全集
     * @param collA 集合A
     * @param compareFun 比较函数
     * @return
     */
    public static <T> List<T> diff(Collection<T> all, Collection<T> collA, BiFunction<T, T, Boolean> compareFun) {
        if (CollectionUtils.isEmpty(collA)) {
            return new ArrayList<>(all);
        }
        return all.stream().filter(ele -> collA.stream().noneMatch(a -> compareFun.apply(ele, a))).collect(Collectors.toList());
    }


    // ---------------------------------- 字符串与集合互转 ---------------------------------------------

    /**
     * 字符串分割后转为list
     * @param str 字符串
     * @param delimiter 分隔符
     * @return
     */
    public static List<String> toList(String str, String delimiter) {
        if (StringUtils.hasText(str)) {
            return new ArrayList<>();
        }
        return Arrays.stream(str.split(delimiter)).collect(Collectors.toList());
    }

    /**
     * 连接字符串
     * @param coll 集合
     * @param delimiter 分隔符
     * @return
     */
    public static <T> String join(Collection<T> coll, String delimiter) {
        return join(coll, Object::toString, delimiter);
    }

    /**
     * 连接字符串
     * @param coll 集合
     * @param strMapper 字符串映射
     * @param delimiter 分隔符
     * @return
     */
    public static <T> String join(Collection<T> coll, Function<T, String> strMapper, String delimiter) {
        return coll.stream().map(strMapper).collect(Collectors.joining(delimiter));
    }



    // ----------------------------------------- 列表分割 --------------------------------------------------------
    /**
     * 对集合按照指定长度分段，每一个段为单独的集合，返回这个集合的列表
     * @param <T>  集合元素类型
     * @param list 列表
     * @param partitionSize 每个分区大小
     * @return 分段列表
     */
    public static <T> List<List<T>> partitionList(List<T> list, int partitionSize) {
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        int listSize = list.size();
        List<List<T>> result = new ArrayList<>(listSize / partitionSize + 1);
        int beginIdx = 0;
        for (int endIdx = partitionSize; endIdx <= listSize; beginIdx = endIdx, endIdx += partitionSize) {
            result.add(list.subList(beginIdx, endIdx));
        }
        if (beginIdx < listSize) {
            result.add(list.subList(beginIdx, listSize));
        }
        return result;
    }


    /**
     * 将集合均分成指定个数的小列表返回，如果不能均分，多余的元素分给前面的集合
     * @param list 列表
     * @param count 分割的数量
     */
    public static <T> List<List<T>> splitList(List<T> list, int count) {
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        int listSize = list.size();
        int baseSize = listSize / count;
        int remainder = listSize % count;
        List<List<T>> result = new ArrayList<>(count);
        // 当前子列表的起始索引
        int fromIndex = 0;
        for (int i = 0; i < count; i++) {
            // 根据余数调整当前子列表的大小
            int partitionSize = baseSize + (i < remainder ? 1 : 0);
            int toIndex = fromIndex + partitionSize;
            result.add(list.subList(fromIndex, Math.min(toIndex, listSize)));
            fromIndex = toIndex;
        }
        return result;
    }




    // ------------------------------------------------ 列表飙升次数判断 ----------------------------------

    /**
     * 计算在给定的列表中，所有可能的、固定大小窗口内连续上升的窗口数量
     * @param dataList 数据列表
     * @param windowsSize 窗口大小
     * @return 飙升次数
     */
    public static <T extends Comparable> int calRisingWindowCounts(List<T> dataList, int windowsSize) {
        if (CollectionUtils.isEmpty(dataList) || dataList.size() < windowsSize) {
            return 0;
        }

        int riseCount = 0;
        for (int i = 0; i <= dataList.size() - windowsSize; i++) {
            boolean isRising = true;
            for (int j = 1; j < windowsSize; j++) {
                if (dataList.get(i + j).compareTo(dataList.get(i + j - 1)) < 0) {
                    isRising = false;
                    break;
                }
            }
            if (isRising) {
                riseCount++;
            }
        }
        return riseCount;
    }


    /**
     * 接收一个List并返回每个连续相同整数的范围的开始索引、结束索引和该整数的值
     * 如 [1, 4, 5, 5, 6, 6, 7]
     * 返回[[0, 0, 1],[1, 1, 4],[2, 3, 5],[4, 5, 6],[6, 6, 7]]
     * @param inputList
     * @return
     */
    public static List<List<Integer>> getRangesWithIndices(List<Integer> inputList) {
        if (CollectionUtils.isEmpty(inputList)) {
            return Collections.emptyList();
        }
        List<List<Integer>> resultList = new ArrayList<>();
        int startIndex = 0;
        for (int i = 1; i < inputList.size(); i++) {
            Integer lastVal = inputList.get(i - 1);
            Integer curVal = inputList.get(i);
            if (!Objects.equals(lastVal, curVal)) {
                resultList.add(Arrays.asList(startIndex, i - 1, lastVal));
                startIndex = i;
            }
        }
        resultList.add(Arrays.asList(startIndex, inputList.size() - 1, inputList.get(inputList.size() - 1)));
        return resultList;
    }
}